//////////////////////////////////////////////////////////////////////////
#pragma once


namespace Math
{
	namespace Simd
	{
		//////////////////////////////////////////////////////////////////////////
		// C++ reference implementation.
		struct Reference
		{
			// Reference type structures.
			// 8 byte types.
			struct I8x8							{int8_t		v[ 8 ];};		static_assert( sizeof( I8x8 )==8, "Incorrect size." );
			struct U8x8							{uint8_t	v[ 8 ];};		static_assert( sizeof( U8x8 )==8, "Incorrect size." );
			struct I16x4						{int16_t	v[ 4 ];};		static_assert( sizeof( I16x4 )==8, "Incorrect size." );
			struct U16x4						{uint16_t	v[ 4 ];};		static_assert( sizeof( U16x4 )==8, "Incorrect size." );
			struct I32x2						{int32_t	v[ 2 ];};		static_assert( sizeof( I32x2 )==8, "Incorrect size." );
			struct U32x2						{uint32_t	v[ 2 ];};		static_assert( sizeof( U32x2 )==8, "Incorrect size." );
			struct F32x2						{float		v[ 2 ];};		static_assert( sizeof( F32x2 )==8, "Incorrect size." );

			// 16 byte types.
			struct I8x16						{int8_t		v[ 16 ];};		static_assert( sizeof( I8x16 )==16, "Incorrect size." );
			struct U8x16						{uint8_t	v[ 16 ];};		static_assert( sizeof( U8x16 )==16, "Incorrect size." );
			struct I16x8						{int16_t	v[ 8 ];};		static_assert( sizeof( I16x8 )==16, "Incorrect size." );
			struct U16x8						{uint16_t	v[ 8 ];};		static_assert( sizeof( U16x8 )==16, "Incorrect size." );
			struct I32x4						{int32_t	v[ 4 ];};		static_assert( sizeof( I32x4 )==16, "Incorrect size." );
			struct U32x4						{uint32_t	v[ 4 ];};		static_assert( sizeof( U32x4 )==16, "Incorrect size." );
			struct F32x4						{float		v[ 4 ];};		static_assert( sizeof( F32x4 )==16, "Incorrect size." );
			struct F64x2						{double		v[ 2 ];};		static_assert( sizeof( F64x2 )==16, "Incorrect size." );

			// 32 byte types.
			struct I8x32						{int8_t		v[ 32 ];};		static_assert( sizeof( I8x32 )==32, "Incorrect size." );
			struct U8x32						{uint8_t	v[ 32 ];};		static_assert( sizeof( U8x32 )==32, "Incorrect size." );
			struct I16x16						{int16_t	v[ 16 ];};		static_assert( sizeof( I16x16 )==32, "Incorrect size." );
			struct U16x16						{uint16_t	v[ 16 ];};		static_assert( sizeof( U16x16 )==32, "Incorrect size." );
			struct I32x8						{int32_t	v[ 8 ];};		static_assert( sizeof( I32x8 )==32, "Incorrect size." );
			struct U32x8						{uint32_t	v[ 8 ];};		static_assert( sizeof( U32x8 )==32, "Incorrect size." );
			struct F32x8						{float		v[ 8 ];};		static_assert( sizeof( F32x8 )==32, "Incorrect size." );
			struct F64x4						{double		v[ 4 ];};		static_assert( sizeof( F64x4 )==32, "Incorrect size." );

			// Typedefs.
			typedef F32x2						F32x2_t;
			typedef F32x4						F32x4_t;
			typedef F32x8						F32x8_t;


			template< typename Type >
			struct Param
			{
				typedef const Type&					Type_t;
			};

			//////////////////////////////////////////////////////////////////////////
			static bool							IsValid( F32x4_t v )
			{
				F32x4_t		test	=	{{v.v[0]*0.0f, v.v[1]*0.0f, v.v[2]*0.0f, v.v[3]*0.0f}};
				return test.v[0]==0.0f && test.v[1]==0.0f && test.v[2]==0.0f && test.v[3]==0.0f;
			}

#if defined( SIMD_ADVANCED_DEBUG )
			static void							Validate( F32x4_t v )	{if( !IsValid( (v) ) ) CORE_BREAK;}
#else
			static void							Validate( F32x4_t v )	{}
#endif

			static F32x4_t						Create( float x, float y, float z )
			{
				F32x4_t		result	=	{{x, y, z, 0.0f}};
				return result;
			}

			static F32x4_t						Create( float x, float y, float z, float w )
			{
				F32x4_t		result	=	{{x, y, z, w}};
				return result;
			}

			static float						Fetch( const F32x4_t& v, size_t idx )
			{
				assert( idx < 4 );
				return v.v[ idx ];
			}

			static F32x4_t						Store( const F32x4_t& v, float f, size_t idx )
			{
				assert( idx < 4 );
				F32x4_t		result	=	v;
				result.v[ idx ]		=	f;
				return result;
			}

			static F32x4_t						Negate( const F32x4_t& v )
			{
				F32x4_t		result;
				result.v[ 0 ]	=	-v.v[ 0 ];
				result.v[ 1 ]	=	-v.v[ 1 ];
				result.v[ 2 ]	=	-v.v[ 2 ];
				result.v[ 3 ]	=	0.0f;
				return result;
			}

			static F32x4_t						Add( const F32x4_t& lhs, const F32x4_t& rhs )
			{
				F32x4_t		result	=	lhs;
				result.v[ 0 ]		+=	rhs.v[ 0 ];
				result.v[ 1 ]		+=	rhs.v[ 1 ];
				result.v[ 2 ]		+=	rhs.v[ 2 ];
				return result;
			}

			static F32x4_t						Sub( const F32x4_t& lhs, const F32x4_t& rhs )
			{
				F32x4_t		result	=	lhs;
				result.v[ 0 ]		-=	rhs.v[ 0 ];
				result.v[ 1 ]		-=	rhs.v[ 1 ];
				result.v[ 2 ]		-=	rhs.v[ 2 ];
				return result;
			}

			static F32x4_t						Mul( const F32x4_t& lhs, const F32x4_t& rhs )
			{
				F32x4_t		result	=	lhs;
				result.v[ 0 ]		*=	rhs.v[ 0 ];
				result.v[ 1 ]		*=	rhs.v[ 1 ];
				result.v[ 2 ]		*=	rhs.v[ 2 ];
				return result;
			}

			static F32x4_t						Div( const F32x4_t& lhs, const F32x4_t& rhs )
			{
				F32x4_t		result	=	lhs;
				result.v[ 0 ]		/=	rhs.v[ 0 ];
				result.v[ 1 ]		/=	rhs.v[ 1 ];
				result.v[ 2 ]		/=	rhs.v[ 2 ];
				return result;
			}

			static float						Dot3( const F32x4_t& lhs, const F32x4_t& rhs )
			{
				return
					(lhs.v[ 0 ] * rhs.v[ 0 ]) +
					(lhs.v[ 1 ] * rhs.v[ 1 ]) +
					(lhs.v[ 2 ] * rhs.v[ 2 ]);
			}

			static float						Mag3( const F32x4_t& lhs )
			{
				return std::sqrt( 
					(lhs.v[ 0 ] * lhs.v[ 0 ])	+
					(lhs.v[ 1 ] * lhs.v[ 1 ])	+
					(lhs.v[ 2 ] * lhs.v[ 2 ])	);
			}

			static F32x4_t						Norm3( const F32x4_t& lhs )
			{
				float	mag	=	 std::sqrt( 
					(lhs.v[ 0 ] * lhs.v[ 0 ])	+
					(lhs.v[ 1 ] * lhs.v[ 1 ])	+
					(lhs.v[ 2 ] * lhs.v[ 2 ])	);
				F32x4_t		result	=	lhs;
				result.v[ 0 ]	/=	mag;
				result.v[ 1 ]	/=	mag;
				result.v[ 2 ]	/=	mag;
				return result;
			}

			static F32x4_t						Cross3( const F32x4_t& lhs, const F32x4_t& rhs )
			{
				F32x4_t	result	=
				{{
					(lhs.v[ 1 ] * rhs.v[ 2 ] - lhs.v[ 2 ] * rhs.v[ 1 ]),
					(lhs.v[ 2 ] * rhs.v[ 0 ] - lhs.v[ 0 ] * rhs.v[ 2 ]),
					(lhs.v[ 0 ] * rhs.v[ 1 ] - lhs.v[ 1 ] * rhs.v[ 0 ]),
					0.0f
				}};
				return result;
			}
		};
	}
}
